#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>

#include "mad.h"
#include "decode.h"

/*
struct buffer {
	int fd;
	unsigned int buflen;
	unsigned int bufpos;
	unsigned char buff[BUFSIZE];
	unsigned int bufsize;
};
*/



static snd_pcm_t *pci_handle = NULL; //PCI设备句柄
static snd_pcm_hw_params_t *pcm_hw_params = NULL;

/*初始化PCM设备*/
int open_pcm_device(void)
{
	int err;
	unsigned int samplerate = SAMPLERATE;
	int channels = CHANNELS;
	

	/*打开PCM设备*/
	err = snd_pcm_open(&pci_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0);
	if (err < 0) {
		perror("snd_pcm_open()");
		return 1;
	}

	/*在栈中分配snd_pcm_hw_params_t结构体的空间*/
	err = snd_pcm_hw_params_malloc(&pcm_hw_params);
	if (err < 0) {
		perror("snd_pcm_hw_params_malloc()");
		return 1;
	}

	/*用声卡的全配置空间参数，初始化已经分配的snd_pcm_hw_params_t结构体*/
	err = snd_pcm_hw_params_any(pci_handle, pcm_hw_params);
	if (err < 0) {
		perror("snd_pcm_hw_params_any()");
		return 1;
	}

	/*设置访问类型，交错访问*/
	err = snd_pcm_hw_params_set_access(pci_handle, pcm_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
	if (err < 0) {
		perror("snd_pcm_hw_params_set_access()");
		return 1;
	}

	/*设置数据格式，有符号32位小端*/
	err = snd_pcm_hw_params_set_format(pci_handle, pcm_hw_params, SND_PCM_FORMAT_S16_LE);
	if (err < 0) {
		perror("snd_pcm_hw_params_set_format()");
		return 1;
	}

	/*设置音频数据最接近的采样率*/
	err = snd_pcm_hw_params_set_rate_near(pci_handle, pcm_hw_params, &samplerate, 0);
	if (err < 0) {
		perror("snd_pcm_hw_params_set_rate_near()");
		return 1;
	}

	/*设置音频设备的声道*/
	err = snd_pcm_hw_params_set_channels(pci_handle, pcm_hw_params, channels);
	if (err < 0) {
		perror("snd_pcm_hw_params_set_channels()");
		return 1;
	}

	/*从设备配置空间选择一个配置，让函数snd_pcm_prepare()准备好PCM设备，以便写入数据*/
	err = snd_pcm_hw_params(pci_handle, pcm_hw_params);
	if (err < 0) {
		perror("snd_pcm_hw_params()");
		return 1;
	}

	return 0;
}

int close_pcm_device()
{
	int err;
	snd_pcm_hw_params_free(pcm_hw_params);
	err = snd_pcm_prepare(pci_handle);
	if (err < 0) {
		perror("snd_pcm_prepare()");
		return 1;
	}

	return 0;
}



static enum mad_flow input(void *data, struct mad_stream *stream)
{
	struct buffer *buffer = data;

	int unproc_data_size;
	int copy_size = 0;
	int re_copy_size;

	//if (buffer->bufpos < buffer->buflen) {
	if (1) {
		unproc_data_size = stream->bufend - stream->next_frame;
		memcpy(buffer->buff, buffer->buff + buffer->bufsize -\
				unproc_data_size, unproc_data_size);

		copy_size = BUFSIZE - unproc_data_size;

		if ((buffer->bufpos + copy_size) > buffer->buflen)
			copy_size = buffer->buflen - buffer->bufpos;

		re_copy_size = read(buffer->fd, buffer->buff + unproc_data_size, copy_size);
		if (re_copy_size <= 0)
			printf("re_copy_size=%d\n", re_copy_size);
		buffer->bufsize = unproc_data_size + re_copy_size;
		buffer->bufpos += re_copy_size;

		mad_stream_buffer(stream, buffer->buff, buffer->bufsize);

		return MAD_FLOW_CONTINUE;
	}

	return MAD_FLOW_STOP;
}

static inline signed int scale(mad_fixed_t sample)
{
	sample += (1L << (MAD_F_FRACBITS - 16));

	if (sample >= MAD_F_ONE)
		sample = MAD_F_ONE - 1;
	else if (sample < -MAD_F_ONE)
		sample = -MAD_F_ONE;

	return sample >> (MAD_F_FRACBITS + 1 -16);
}

int xrun_recovery(snd_pcm_t *handle, int err)
{
	if (err == -EPIPE) {
		/* under-run */
		err = snd_pcm_prepare(handle);
		if (err < 0) {
			perror("xrun_recovery:EPIPE:snd_pcm_prepare()");
			return 0;
		}
	} else if(err == -ESTRPIPE) {
		while ((err = snd_pcm_resume(handle)) == -EAGAIN)
			sleep(1);
		if (err < 0) {
			err = snd_pcm_prepare(handle);
			if (err < 0) {
				perror("xrun_recovery:ESTRPIPE:snd_pcm_prepare()");
			}
		}
		return 0;
	}
	return err;
}

static enum mad_flow output(void *data,\
		struct mad_header const *header, struct mad_pcm *pcm)
{
	unsigned int nchannels, nsamples, n;
	mad_fixed_t const *left_ch, *right_ch;

	nchannels = pcm->channels;
	n = nsamples = pcm->length;
	left_ch = pcm->samples[0];
	right_ch = pcm->samples[1];


	unsigned char Output[6912], *OutputPtr;
	int err;


	OutputPtr = Output;

	while (nsamples--) {
		signed int sample;
		sample = scale(*left_ch++);
		*(OutputPtr++) = sample & 0xff;
		*(OutputPtr++) = (sample >> 8) & 0xff;

		if (nchannels == 2) {
			sample = scale(*right_ch++);
			*(OutputPtr++) = sample & 0xff;
			*(OutputPtr++) = (sample >> 8) & 0xff;
		}
	}

	OutputPtr = Output;
	err = snd_pcm_writei(pci_handle, OutputPtr, n);
	if (err < 0) {
		err = xrun_recovery(pci_handle, err);
		if (err < 0) {
			perror("xrun_recovery()");
			exit(1);
		}
	}
	OutputPtr = Output;

	return MAD_FLOW_CONTINUE;

}

static enum mad_flow error(void *data,\
		 struct mad_stream *stream, struct mad_frame *frame)
{
	struct buffer *buffer = data;
	fprintf(stderr, "decoding error 0x%x (%s) at byte offset %ld\n",\
			stream->error, mad_stream_errorstr(stream),\
			stream->this_frame - buffer->buff);

	return MAD_FLOW_CONTINUE;	
}


int decode(struct buffer *buffer)
{
	struct mad_decoder decoder;
	int result;

	printf("mad_init\n");
	mad_decoder_init(&decoder, buffer, input, 0, 0, output, error, 0);

	printf("run\n");
	result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);

	mad_decoder_finish(&decoder);

	return result;
}
